#include "stdafx.h"
#include "BrassicaPrivate.h"

//Pipe Controller
//Creates and destroys pipes, and handles all calls to read from and write to them

void doubletablesize();
PIPEHANDLE newhandle(PipeEnd *contents);
void deletehandle(PIPEHANDLE hPipe);

//Thread safety is paramount
HANDLE hMutex = NULL;
PipeEnd **table = NULL; //table of handle dereferences
UINT32 tablesize;

void initpipecontroller(){
	if ((hMutex = CreateMutex(NULL, FALSE, NULL)) == NULL){
		throw "Error creating mutex";
	}
	tablesize = 16;
	if ((table = (PipeEnd**)calloc(tablesize, sizeof(VOID*))) == NULL){
		throw "Error allocating memory";
	}
}

void destroypipecontroller(){
	if (hMutex == NULL){
		throw "Pipe controller not yet initiated";
	}
	CloseHandle(hMutex);
	free(table);
	hMutex = NULL;
}

PIPEHANDLE newhandle(PipeEnd *contents){
//see if there are any gaps and put the new handle in the first one
	if (hMutex == NULL){
		throw "Pipe controller not yet initiated";
	}
	UINT32 i;
	for (i = 0; i < tablesize; i++){
		if (table[i] == NULL)
			goto foundloc;
	}
	//needs moar space
	doubletablesize();
foundloc:
	table[i] = contents;
	contents->hPipe = i;
	return i;
}

void deletehandle(PIPEHANDLE hPipe){
	if (hMutex == NULL){
		throw "Pipe controller not yet initiated";
	}
	if (hPipe >= tablesize){
		throw "Invalid handle";
	}
	Pipe *p = table[hPipe]->pipe;
	table[p->inpipe->hPipe] = NULL;
	table[p->outpipe->hPipe] = NULL;
	delete p; //this frees the two PipeEnd objects
}

void doubletablesize(){
	if (hMutex == NULL){
		throw "Pipe controller not yet initiated";
	}
	tablesize <<= 1;
	if ((table = (PipeEnd**)realloc(table, tablesize * sizeof(VOID*))) == NULL){
		throw "Error allocating memory";
	}
	memset((VOID*)((UINT_PTR)table + (tablesize >> 2) * sizeof(VOID*)), 0, tablesize >> 2);
}

VOID createpipe(PIPEHANDLE *hPipeIn, PIPEHANDLE *hPipeOut){
	//Get the mutex
	if (WaitForSingleObject(hMutex, INFINITE) != WAIT_OBJECT_0){
		throw "Error securing mutex";
	}

	PipeIn *pPipeIn;
	PipeOut *pPipeOut;
	Pipe *p = new Pipe(&pPipeIn, &pPipeOut);

	*hPipeIn = newhandle(pPipeIn);
	*hPipeOut = newhandle(pPipeOut);

	if (!ReleaseMutex(hMutex)){
		throw "Error releasing mutex";
	}
}

VOID destroypipe(PIPEHANDLE hPipeEnd){
	//Get the mutex
	if (WaitForSingleObject(hMutex, INFINITE) != WAIT_OBJECT_0){
		throw "Error securing mutex";
	}
	deletehandle(hPipeEnd);
	if (!ReleaseMutex(hMutex)){
		throw "Error releasing mutex";
	}
}

SIZE_T BRASSICA_API PipeRead(PIPEHANDLE hPipe, VOID* buffer, SIZE_T maxlen){
	if (hMutex == NULL){
		throw "Pipe controller not yet initiated";
	}
	//Get the mutex
	if (WaitForSingleObject(hMutex, INFINITE) != WAIT_OBJECT_0){
		throw "Error securing mutex";
	}

	SIZE_T num = ((PipeOut*)table[hPipe])->Read(buffer, maxlen);

	if (!ReleaseMutex(hMutex)){
		throw "Error releasing mutex";
	}

	return num;
}
VOID BRASSICA_API PipeWrite(PIPEHANDLE hPipe, VOID* data, SIZE_T datalen){
	if (hMutex == NULL){
		throw "Pipe controller not yet initiated";
	}

	//Get the mutex
	if (WaitForSingleObject(hMutex, INFINITE) != WAIT_OBJECT_0){
		throw "Error securing mutex";
	}

	((PipeIn*)table[hPipe])->Write(data, datalen);

	if (!ReleaseMutex(hMutex)){
		throw "Error releasing mutex";
	}
}
